package freedom.ms.gate;

import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.nio.charset.Charset;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.util.ClassUtils;

import com.google.common.hash.Hashing;

public class CacheKeyGenerator implements KeyGenerator {
	Logger logger = LoggerFactory.getLogger(CacheKeyGenerator.class);

	// custom cache key
	public static final String NO_PARAM_KEY = "NO_PARAM";
	public static final String NULL_PARAM_KEY = "NULL_PARAM";

	@Override
	public Object generate(Object target, Method method, Object... params) {

		StringBuilder key = new StringBuilder();
		key.append(target.getClass().getSimpleName()).append(".").append(method.getName()).append(":");
		if (params.length == 0) {
			return key.append(NO_PARAM_KEY).toString();
		}
		for (int k = 0; k < params.length; k++) {
			Object param = params[k];
			if (param == null) {
				logger.warn("input null param for Spring cache, use default key={}", NULL_PARAM_KEY);
				key.append(NULL_PARAM_KEY);
			} else if (ClassUtils.isPrimitiveArray(param.getClass())) {
				int length = Array.getLength(param);
				for (int i = 0; i < length; i++) {
					key.append(Array.get(param, i));
					key.append(',');
				}
			} else if (ClassUtils.isPrimitiveOrWrapper(param.getClass()) || param instanceof String) {
				key.append(param);
			} else {
				logger.warn("Using an object as a cache key may lead to unexpected results. "
						+ "Either use @Cacheable(key=..) or implement CacheKey. Method is " + target.getClass() + "#"
						+ method.getName());
				key.append(param.hashCode());
			}
			if (k != params.length - 1)
				key.append('-');
		}
		String finalKey = key.toString();
		long cacheKeyHash = Hashing.murmur3_128().hashString(finalKey, Charset.defaultCharset()).asLong();
		logger.debug("using cache key={} hashCode={}", finalKey, cacheKeyHash);
		return key.toString();
	}

}
